#include "Engine.h"
#include "object.h"
#include <cl/opencl.h>
#include "cl_triangle.h"
#include "clstate.h"
#include <cstdio>
#include <gl/gl3.h>

#include <cl/cl.h>

///create lightstate class


Engine::Engine()
{

    c_pos(0,0,0);
    c_rot(0,0,0);
    //ambientfactor=0.5;

}



cl_mem Engine::gtriangles;
cl_mem Engine::gisdrawbuffer;
cl_mem Engine::atomic_id_buffer;
unsigned int Engine::itriangles=0;
cl_mem Engine::image3d;
cl_mem gtextemp;
unsigned int Engine::gltscreen_id=0;
unsigned int Engine::framebuffer=0;

cl_uint *Engine::gid_bufferiswritten;
cl_uint *Engine::gid_bufferisread;
cl_uint *Engine::atomic_buffer_read;






void Engine::load(int psw, int psh, int psd, bool fullscr)
{
    //cstate=true;
    screenwidth=psw;
    screenheight=psh;
    screendepth=psd;

    if(fullscr)
        window.Create(sf::VideoMode(screenwidth, screenheight), "No Clues Here Watson", sf::Style::Fullscreen);

    else
        window.Create(sf::VideoMode(screenwidth, screenheight), "No Clues Here Watson");


    screen.Create(screenwidth, screenheight);

    //clf_screenr=new cl_float[screenwidth*screenheight];
    //clf_screeng=new cl_float[screenwidth*screenheight];
    //clf_screenb=new cl_float[screenwidth*screenheight];
    clf_screena=new cl_uint[screenwidth*screenheight];

    for(int i=0; i<screenwidth*screenheight; i++)
    {
        clf_screena[i]=UINT_MAX;
    }



    //clfm_screenr=clCreateBuffer(cl::context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, sizeof(cl_float)*screenwidth*screenheight, (clf_screenr), &cl::error); ///pinned memory -- CL_MEM_ALLOC_HOST_PTR
    //clfm_screeng=clCreateBuffer(cl::context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, sizeof(cl_float)*screenwidth*screenheight, (clf_screeng), &cl::error); ///CL_MEM_USE_HOST_PTR. Actually write data across pci bus, rather than pass whole object there and back. Seems to be faster
    //clfm_screenb=clCreateBuffer(cl::context, CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, sizeof(cl_float)*screenwidth*screenheight, (clf_screenb), &cl::error); ///also CL_MEM_COPY_HOST_PTR
    clfm_screena=clCreateBuffer(cl::context, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, sizeof(cl_uint)*screenwidth*screenheight, (clf_screena), &cl::error);

    ///clEnqueueMapBuffer can be used to access cl_mem normally


    cam_pos=new cl_float4;
    cam_rot=new cl_float4;

    cl_screenwidth = new cl_uint; ///move all memory allocation outside loop
    cl_screenheight = new cl_uint;

    *cl_screenwidth=screenwidth;
    *cl_screenheight=screenheight;

    cl_uint *temp=new cl_uint;

    tcount=clCreateBuffer(cl::context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(cl_uint), (temp), &cl::error);

    delete temp;


    sf::Image img;
    img.LoadFromFile("aaaarg.png");

    cl_float4 *texture=new cl_float4[img.GetHeight()*img.GetWidth()];
    blank=new cl_float4[1024*1024];
    cl_float4 *blank_id=new cl_float4[1024*1024];
    //blank_depthindicator=new cl_float4[1024*1024];

    int tex_width=img.GetHeight();
    int tex_height=img.GetWidth();



    for(int i=0; i<tex_width; i++)
    {
        for(int j=0; j<tex_height; j++)
        {
            sf::Color pixel;
            pixel=img.GetPixel(i, j);
            texture[i + tex_width * j].x=pixel.r;
            texture[i + tex_width * j].y=pixel.g;
            texture[i + tex_width * j].z=pixel.b;
            texture[i + tex_width * j].w=1;


        }
    }

    for(int i=0; i<1024*1024; i++){
        blank[i].x=0;
        blank[i].y=0;
        blank[i].z=0;
        blank[i].w=0;
        blank_id[i].x=-1;
        //blank_depthindicator[i].x=0;
       // blank_depthindicator[i].y=0;
        //blank_depthindicator[i].z=0;
        //blank_depthindicator[i].w=0;
    }


    cl_image_format fermat;
    fermat.image_channel_order=CL_RGBA;
    fermat.image_channel_data_type=CL_FLOAT;

    gtextemp=clCreateImage2D(cl::context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, &fermat, 1024, 1024, 0, texture, NULL);

    light_normals=clCreateImage2D(cl::context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, &fermat, 1024, 1024, 0, blank, NULL);
    uvw_coords=clCreateImage2D(cl::context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, &fermat, 1024, 1024, 0, blank, &cl::error);
    depth_vanilla=clCreateImage2D(cl::context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, &fermat, 1024, 1024, 0, blank, &cl::error);
    id_triangles=clCreateImage2D(cl::context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, &fermat, 1024, 1024, 0, blank_id, &cl::error);

    if(cl::error!=0){
        std::cout << "image creation (Engine.cpp uvw_coords)" << std::endl;
        exit(cl::error);
    }
    delete [] texture;

    lframenum=0;

}


void Engine::init_glcl()
{


    PFNGLGENFRAMEBUFFERSEXTPROC glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)wglGetProcAddress("glGenFramebuffersEXT");
    PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)wglGetProcAddress("glBindFramebufferEXT");
    PFNGLGENRENDERBUFFERSEXTPROC glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC)wglGetProcAddress("glGenRenderbuffersEXT");
    PFNGLBINDRENDERBUFFEREXTPROC glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC)wglGetProcAddress("glBindRenderbufferEXT");
    PFNGLRENDERBUFFERSTORAGEEXTPROC glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC)wglGetProcAddress("glRenderbufferStorageEXT");
    PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)wglGetProcAddress("glFramebufferRenderbufferEXT");
    PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC)wglGetProcAddress("glBlitFramebufferEXT");
    PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)wglGetProcAddress("glCheckFramebufferStatusEXT");


    glGenRenderbuffersEXT(1, &gltscreen_id);
    glBindRenderbufferEXT(GL_RENDERBUFFER, gltscreen_id);

    glRenderbufferStorageEXT(GL_RENDERBUFFER, GL_RGBA, 2048, 2048); ///find nearest power of two to screen size and use that.



    glGenFramebuffersEXT(1, &framebuffer);
    glBindFramebufferEXT(GL_FRAMEBUFFER, framebuffer);


    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gltscreen_id);


    gscreen_rgb=clCreateFromGLRenderbuffer(cl::context, CL_MEM_WRITE_ONLY, gltscreen_id, &cl::error);

    if(cl::error!=0)
    {
        std::cout << "Error: CL/GL interop memory alloc" << std::endl;
        exit(cl::error);
    }


}

Engine::~Engine()
{

    //delete [] clf_screenr;
    //delete [] clf_screeng;
    //delete [] clf_screenb;
    delete [] clf_screena;


    //clReleaseMemObject(clfm_screenr);
    //clReleaseMemObject(clfm_screeng);
    //clReleaseMemObject(clfm_screenb);
    clReleaseMemObject(clfm_screena);

    delete [] cam_pos;
    delete [] cam_rot;
    delete [] cl_screenwidth;
    delete [] cl_screenheight;

}








void Engine::draw_gobjects_cl()
{




    sf::Clock CPUTime;
    CPUTime.Reset();

    double timeone, timetwo;

    //int num_tri=T->max_tnum;
    unsigned int num_tri=Engine::itriangles;

    cam_pos[0].x=c_pos.x;
    cam_pos[0].y=c_pos.y;
    cam_pos[0].z=c_pos.z;
    cam_rot[0].x=c_rot.x;
    cam_rot[0].y=c_rot.y;
    cam_rot[0].z=c_rot.z;

    bool msf=false; ///kernel time output.
    bool triangle_cull_debugging=false;
    bool state_debugging=false;


    const unsigned int block_size=num_tri;

    cl_bool state=false;
    ///the arbitrary value is every triangle draws less than 4 pixels
    if(lframenum>screenwidth*screenheight*0.25)
    {
        state=true;
    }
    //state=true;

    if(state_debugging)
    {
        std::cout << state << std::endl;
    }

    cl_mem clcm_pos = clCreateBuffer(cl::context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(cl_float4), cam_pos, &cl::error); ///could be easily changed to a global variable
    cl_mem clcm_rot = clCreateBuffer(cl::context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(cl_float4), cam_rot, &cl::error);
    cl_mem clam_width = clCreateBuffer(cl::context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(int), cl_screenwidth, &cl::error);
    cl_mem clam_height = clCreateBuffer(cl::context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(int), cl_screenheight, &cl::error);
    cl_uint cl_drawcount=0;
    cl_mem clim_drawcount = clCreateBuffer(cl::context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(cl_uint), &cl_drawcount, &cl::error);
    cl_mem clbm_isstate = clCreateBuffer(cl::context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(cl_bool), &state, &cl::error);

    cl_uint *t=new cl_uint; ///sigh.
    *t=num_tri;
    clEnqueueWriteBuffer(cl::cqueue, tcount, CL_TRUE, 0, sizeof(cl_uint), t, 0, NULL, NULL);

    if(triangle_cull_debugging)
    {
        std::cout << "Triangle num:" << num_tri << std::endl;
    }



    cl::error |= clSetKernelArg(cl::kernel, 0, sizeof(cl_mem), &clcm_pos); ///camera pos
    cl::error |= clSetKernelArg(cl::kernel, 1, sizeof(cl_mem), &clcm_rot); ///camera rotation
    cl::error |= clSetKernelArg(cl::kernel, 2, sizeof(cl_mem), &clam_width); ///screen width
    cl::error |= clSetKernelArg(cl::kernel, 3, sizeof(cl_mem), &clam_height);///screen height
    cl::error |= clSetKernelArg(cl::kernel, 4, sizeof(cl_mem), &Engine::gtriangles); ///compound different objects together. Beforehand? SET UP GLOBAL MANAGER HUEHUEHUE
    cl::error |= clSetKernelArg(cl::kernel, 5, sizeof(cl_mem), &clfm_screena);
    cl::error |= clSetKernelArg(cl::kernel, 6, sizeof(cl_mem), &glights);
    cl::error |= clSetKernelArg(cl::kernel, 7, sizeof(cl_mem), &glnum);
    cl::error |= clSetKernelArg(cl::kernel, 8, sizeof(cl_mem), &Engine::gisdrawbuffer);
    cl::error |= clSetKernelArg(cl::kernel, 9, sizeof(cl_mem), &tcount);
    cl::error |= clSetKernelArg(cl::kernel, 10, sizeof(cl_mem), &clim_drawcount);
    cl::error |= clSetKernelArg(cl::kernel, 11, sizeof(cl_mem), &atomic_id_buffer);
    cl::error |= clSetKernelArg(cl::kernel, 12, sizeof(cl_mem), &clbm_isstate);
    //cl::error |= clSetKernelArg(cl::kernel, 12, sizeof(cl_mem), &clim_ulong);

    if(cl::error!=0)
    {
        exit(cl::error);
    }


    size_t global_ws = num_tri;
    int cstat=4;
    if(!state){
        cstat=1;
    }
    size_t local1=(512*cstat);

    if(global_ws % local1!=0)
    {

        int rem=global_ws % local1;

        global_ws-=(rem);
        global_ws+=local1;


    }



    sf::Clock c2;
    c2.Reset();
    //exit(1);
    global_ws/=cstat;

    cl::error = clEnqueueNDRangeKernel(cl::cqueue, cl::kernel, 1, NULL, &global_ws, 0, 0, NULL, NULL);

    clFinish(cl::cqueue);

    //clEnqueueReadBuffer(cl::cqueue, Engine::atomic_id_buffer, CL_TRUE,  0, sizeof(cl_uint)*Engine::itriangles, Engine::atomic_buffer_read, 0, NULL, NULL);
    cl_uint numtodraw=0;
    clEnqueueReadBuffer(cl::cqueue, clim_drawcount, CL_TRUE,  0, sizeof(cl_uint), &numtodraw, 0, NULL, NULL);



    if(msf)
        std::cout << "t1: " << (timeone=c2.GetElapsedTime()) << std::endl;

    sf::Clock k3;
    k3.Reset();

    if(numtodraw % local1!=0)
    {

        int rem=numtodraw % local1;

        numtodraw-=(rem);
        numtodraw+=local1;


    }



    //std::cout << numtodraw << std::endl;
    cl::error |= clSetKernelArg(cl::kernel3, 0, sizeof(cl_mem), &clcm_pos); ///camera pos
    cl::error |= clSetKernelArg(cl::kernel3, 1, sizeof(cl_mem), &clcm_rot); ///camera rotation
    cl::error |= clSetKernelArg(cl::kernel3, 2, sizeof(cl_mem), &clam_width); ///screen width
    cl::error |= clSetKernelArg(cl::kernel3, 3, sizeof(cl_mem), &clam_height);///screen height
    cl::error |= clSetKernelArg(cl::kernel3, 4, sizeof(cl_mem), &Engine::gtriangles); ///compound different objects together. Beforehand? SET UP GLOBAL MANAGER HUEHUEHUE
    cl::error |= clSetKernelArg(cl::kernel3, 5, sizeof(cl_mem), &clfm_screena);
    cl::error |= clSetKernelArg(cl::kernel3, 6, sizeof(cl_mem), &glights);
    cl::error |= clSetKernelArg(cl::kernel3, 7, sizeof(cl_mem), &glnum);
    cl::error |= clSetKernelArg(cl::kernel3, 8, sizeof(cl_mem), &Engine::gisdrawbuffer);
    cl::error |= clSetKernelArg(cl::kernel3, 9, sizeof(cl_mem), &tcount);
    cl::error |= clSetKernelArg(cl::kernel3, 10, sizeof(cl_mem), &light_normals);
    cl::error |= clSetKernelArg(cl::kernel3, 11, sizeof(cl_mem), &uvw_coords);
    cl::error |= clSetKernelArg(cl::kernel3, 12, sizeof(cl_mem), &depth_vanilla);
    cl::error |= clSetKernelArg(cl::kernel3, 13, sizeof(cl_mem), &clim_drawcount);
    cl::error |= clSetKernelArg(cl::kernel3, 14, sizeof(cl_mem), &Engine::atomic_id_buffer);

    if(numtodraw>0)
    cl::error = clEnqueueNDRangeKernel(cl::cqueue, cl::kernel3, 1, NULL, &numtodraw, 0, 0, NULL, NULL);
    clFinish(cl::cqueue);

    lframenum=numtodraw;

    if(msf)
        std::cout << "k3: " << (k3.GetElapsedTime()) << std::endl;


    if(cl::error!=0)
    {
        exit(cl::error);
    }


    sf::Clock timer;
    timer.Reset();
    glFinish();
    clEnqueueAcquireGLObjects(cl::cqueue, 1, &gscreen_rgb, 0, NULL, NULL);
    clFinish(cl::cqueue);


    cl::error |= clSetKernelArg(cl::klight, 0, sizeof(cl_mem), &clcm_pos); ///camera pos
    cl::error |= clSetKernelArg(cl::klight, 1, sizeof(cl_mem), &clcm_rot); ///camera pos
    cl::error |= clSetKernelArg(cl::klight, 2, sizeof(cl_mem), &clam_width); ///screen width
    cl::error |= clSetKernelArg(cl::klight, 3, sizeof(cl_mem), &clam_height);///screen height
    cl::error |= clSetKernelArg(cl::klight, 4, sizeof(cl_mem), &glights);
    cl::error |= clSetKernelArg(cl::klight, 5, sizeof(cl_mem), &glnum);
    cl::error |= clSetKernelArg(cl::klight, 6, sizeof(cl_mem), &gscreen_rgb);
    cl::error |= clSetKernelArg(cl::klight, 7, sizeof(cl_mem), &light_normals);
    cl::error |= clSetKernelArg(cl::klight, 8, sizeof(cl_mem), &uvw_coords);
    cl::error |= clSetKernelArg(cl::klight, 9, sizeof(cl_mem), &clfm_screena);
    cl::error |= clSetKernelArg(cl::klight, 10, sizeof(cl_mem), &image3d);
    cl::error |= clSetKernelArg(cl::klight, 11, sizeof(cl_mem), &depth_vanilla);

    size_t worksize[2]={screenwidth, screenheight}; ///make these powers of two; ///nah

    cl::error = clEnqueueNDRangeKernel(cl::cqueue, cl::klight, 2, NULL, worksize, 0, 0, NULL, NULL);
    clFinish(cl::cqueue);
    //exit(1);
    if(cl::error!=0){
        std::cout << "Error In kernel light" << std::endl;
        exit(cl::error);
    }



    clFinish(cl::cqueue);



    clEnqueueReleaseGLObjects(cl::cqueue, 1, &gscreen_rgb, 0, NULL, NULL);
    clFinish(cl::cqueue);
    glFinish();

    if(msf)
        std::cout << "tlight: " << (timetwo=timer.GetElapsedTime()) << std::endl;



    if(cl::error!=0)
    {
        std::cout << "Kernel error in Engine.cpp draw_gobjects_cl" << std::endl;
        exit(cl::error);
    }

    clReleaseMemObject(clcm_pos);
    clReleaseMemObject(clcm_rot);
    clReleaseMemObject(clam_width);
    clReleaseMemObject(clam_height);
    clReleaseMemObject(clim_drawcount);
    delete t;
    //delete [] temp11;
    //delete [] temp12;

    if(msf)
    {

    }
    //std::cout << "total: " << CPUTime.GetElapsedTime() << std::endl;
}





void Engine::screen_fill(int w, int h, int x, int y) ///obsolete
{

    for(int i=x; i<w; i++)
    {
        for(int j=y; j<h; j++)
        {

            sf::Color r(0,0,0,255);

            r.r=clf_screenr[i + screenwidth*j];
            r.g=clf_screeng[i + screenwidth*j];
            r.b=clf_screenb[i + screenwidth*j];

            clf_screenr[i+screenwidth*j]=0;
            clf_screeng[i+screenwidth*j]=0;
            clf_screenb[i+screenwidth*j]=0;

            screen.SetPixel(i, j, r);

            clf_screena[i+screenwidth*j]=UINT_MAX;

        }
    }
}



int perf=0;


void Engine::draw_objects()
{




    ///start object sequence

    ///push clean buffers to graphics card. Except these actually reside in host memory. Dun dun duuuuuuuun.
    //clEnqueueWriteBuffer(cl::cqueue, clfm_screenr, CL_FALSE, 0, screenwidth*screenheight*sizeof(cl_float), clf_screenr, 0, NULL, NULL); ///partition screen into chunks within cl.cl and do shit
    //clEnqueueWriteBuffer(cl::cqueue, clfm_screeng, CL_FALSE, 0, screenwidth*screenheight*sizeof(cl_float), clf_screeng, 0, NULL, NULL); ///nevermind, impossible now that this is the screen, without passing some sort of "THIS IS THE FIRST OBJECT" thing
    //clEnqueueWriteBuffer(cl::cqueue, clfm_screenb, CL_FALSE, 0, screenwidth*screenheight*sizeof(cl_float), clf_screenb, 0, NULL, NULL); ///Tried it - turns out, workgroups (chunks of threads) execute individually with there being no possibility to sync (because 1 executes, then 2, then 3 etc).
    //clEnqueueWriteBuffer(cl::cqueue, clfm_screena, CL_FALSE, 0, screenwidth*screenheight*sizeof(cl_uint), clf_screena, 0, NULL, NULL); ///Or perhaps get each thread to clear pixels with a default depth. Dun dun duuun

    /*cl_int clEnqueueWriteImage (cl_command_queue command_queue,
 	cl_mem image,
 	cl_bool blocking_write,
 	const size_t origin[3],
 	const size_t region[3],
 	size_t input_row_pitch,
 	size_t input_slice_pitch,
 	const void * ptr,
 	cl_uint num_events_in_wait_list,
 	const cl_event *event_wait_list,
 	cl_event *event)*/
    //sf::Clock T;
 	//size_t origin[3]={0,0,0};
 	//size_t region[3]={1024,1024,1};
 	//clEnqueueWriteImage(cl::cqueue, light_normals, CL_TRUE, origin, region, 0, 0, blank, 0, NULL, NULL);
 	//clEnqueueWriteImage(cl::cqueue, uvw_coords, CL_TRUE, origin, region, 0, 0, blank, 0, NULL, NULL);
 	//clEnqueueWriteImage(cl::cqueue, depth_vanilla, CL_TRUE, origin, region, 0, 0, blank, 0, NULL, NULL);

    //clFinish(cl::cqueue);
    //std::cout << T.GetElapsedTime() << std::endl;
    ///nvidia driver may or may not force these to happen synchronously anyway. It barely makes any difference, but it means that in the future things *could* be done in between writing data.clearing the screen, and adding the object.





    if(itriangles>0)
    {
        gmemory_mutex.lock();
        draw_gobjects_cl();
        gmemory_mutex.unlock();
    }

    ///this is no longer per object









    //cl::error=clEnqueueReadBuffer(cl::cqueue, clfm_screenr, CL_FALSE, 0, sizeof(cl_float)*screenwidth*screenheight, clf_screenr, 0, NULL, NULL);
    //cl::error=clEnqueueReadBuffer(cl::cqueue, clfm_screeng, CL_FALSE, 0, sizeof(cl_float)*screenwidth*screenheight, clf_screeng, 0, NULL, NULL);
    //cl::error=clEnqueueReadBuffer(cl::cqueue, clfm_screenb, CL_FALSE, 0, sizeof(cl_float)*screenwidth*screenheight, clf_screenb, 0, NULL, NULL);
    //cl::error=clEnqueueReadBuffer(cl::cqueue, clfm_screena, CL_FALSE, 0, sizeof(cl_float)*screenwidth*screenheight, clf_screena, 0, NULL, NULL);

    //clFinish(cl::cqueue);

    ///end object sequence







}

void Engine::render_buffers()
{



    PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)wglGetProcAddress("glBindFramebufferEXT");

    PFNGLBLITFRAMEBUFFEREXTPROC glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC)wglGetProcAddress("glBlitFramebufferEXT");

    glBindFramebufferEXT(GL_READ_FRAMEBUFFER, framebuffer);
    glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0);


    glBlitFramebufferEXT(0,0,screenwidth, screenheight, 0, 0, screenwidth, screenheight, GL_COLOR_BUFFER_BIT, GL_NEAREST);


    glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, framebuffer);
    glClear(GL_COLOR_BUFFER_BIT);



}



void Engine::add_light(cl_float4 plpos, cl_float4 plcol, float rad)
{

    plpos.w=rad;
    lightpos.push_back(plpos);
    lightcol.push_back(plcol);

    //lightrad.push_back(rad);

}


void Engine::add_light(cl_float4 plpos, cl_float4 plcol)
{

    //plcol.x/=4.0;
    //plcol.y/=4.0;
    //plcol.z/=4.0;
    lightpos.push_back(plpos);
    lightcol.push_back(plcol);


}


void Engine::gpush_lights()
{

    cl_uint *len=new cl_uint;
    *len=lightpos.size();



    cl_float4 *everything = new cl_float4[(*len)*3];

    std::list<cl_float4>::iterator it=lightpos.begin();
    std::list<cl_float4>::iterator it2=lightcol.begin();

    for(unsigned int i=0; i<*len; i++, it++, it2++)
    {

        float depth_far=7000;
        cl_float4 lrot;
        lrot.x=cos(c_rot.y)*(sin(c_rot.z)+cos(c_rot.z)*((*it).x-c_pos.x)) - sin(c_rot.y)*((*it).z-c_pos.z);
        lrot.y=sin(c_rot.x)*(cos(c_rot.y)*((*it).z-c_pos.z)+sin(c_rot.y)*(sin(c_rot.z)*((*it).y-c_pos.y)+cos(c_rot.z)*((*it).x-c_pos.x)))+cos(c_rot.x)*(cos(c_rot.z)*((*it).y-c_pos.y)-sin(c_rot.z)*((*it).x-c_pos.x));
        lrot.z=cos(c_rot.x)*(cos(c_rot.y)*((*it).z-c_pos.z)+sin(c_rot.y)*(sin(c_rot.z)*((*it).y-c_pos.y)+cos(c_rot.z)*((*it).x-c_pos.x)))-sin(c_rot.x)*(cos(c_rot.z)*((*it).y-c_pos.y)-sin(c_rot.z)*((*it).x-c_pos.x));

        float rx;
        rx=lrot.x * (700.0/lrot.z);
        float ry;
        ry=lrot.y * (700.0/lrot.z);

        rx+=screenwidth/2.0;
        ry+=screenheight/2.0;


        lrot.x=rx;
        lrot.y=ry;
        //lrot.z=(log(lrot.z + 1)/log(depth_far + 1));
        lrot.w=(*it).w;

        //std::cout << "cam" << lrot.z << std::endl;



        //everything[i*2]=(*it);
        everything[i*2]=lrot;
        everything[(i*2)+1]=(*it2);
        everything[(i*2)+2]=(*it);
    }

    glights=clCreateBuffer(cl::context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(cl_float4)*(*len)*3, everything, &cl::error); ///keep memory on card and just constantly write new data? Would be a hard limit for number of lights them. Hmm
    glnum=  clCreateBuffer(cl::context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(cl_uint)           , len,        &cl::error);




    delete [] everything;
    delete [] len;



}



void Engine::grem_lights()
{

    clReleaseMemObject(glights);
    clReleaseMemObject(glnum);

}






void Engine::render()
{
    window.Display();
    window.Clear();
}

void Engine::set_ambiance(cl_float r, cl_float g, cl_float b, cl_float brightness)
{

    ambientcolour.x=r*brightness;
    ambientcolour.y=g*brightness;
    ambientcolour.z=b*brightness;
    //ambientcolour=ambientcolour;
    //ambientfactor=pambience;
}

v3d rot(double x, double y, double z, v3d rotation)
{

    double i0x=x;
    double i0y=y;
    double i0z=z;

    double i1x=i0x;
    double i1y=i0y*cos(rotation.x) - sin(rotation.x)*i0z;
    double i1z=i0y*sin(rotation.x) + cos(rotation.x)*i0z;


    double i2x=i1x*cos(rotation.y) + i1z*sin(rotation.y);
    double i2y=i1y;
    double i2z=-i1x*sin(rotation.y) + i1z*cos(rotation.y);

    double i3x=i2x*cos(rotation.z) - i2y*sin(rotation.z);
    double i3y=i2x*sin(rotation.z) + i2y*cos(rotation.z);
    double i3z=i2z;

    v3d ret(i3x, i3y, i3z);

    return ret;


}

void Engine::input()
{



        sf::Event Event;

        while (window.PollEvent(Event))
        {
            if (Event.Type == sf::Event::Closed)
                window.Close();

        }


        double frametime=window.GetFrameTime();

        static int mult=1;
        if(keyboard.IsKeyPressed(sf::Keyboard::LShift))
        {
            mult=10;
        }
        else
        {
            mult=1;
        }

        double distance=0.04*frametime*mult;

        if(keyboard.IsKeyPressed(sf::Keyboard::W))  ///get engine to do this itself. Engine.ProcessInput
        {
            v3d t=rot(0, 0, distance, c_rot);
            c_pos+=t;
        }
        if(keyboard.IsKeyPressed(sf::Keyboard::S))
        {
           v3d t=rot(0, 0, -distance, c_rot);
           c_pos+=t;
        }
        if(keyboard.IsKeyPressed(sf::Keyboard::A))
        {
            //c_pos.x-=0.04*frametime*mult;
            v3d t=rot(-distance, 0, 0, c_rot);
            c_pos+=t;
        }
        if(keyboard.IsKeyPressed(sf::Keyboard::D))
        {
            v3d t=rot(distance, 0, 0, c_rot);
            c_pos+=t;
        }
        if(keyboard.IsKeyPressed(sf::Keyboard::E))
        {
            c_pos.y-=0.04*frametime*mult;
        }
        if(keyboard.IsKeyPressed(sf::Keyboard::Q))
        {
            c_pos.y+=0.04*frametime*mult;
        }

        if(keyboard.IsKeyPressed(sf::Keyboard::Left))
        {
            c_rot.y-=0.001*frametime;
        }
        if(keyboard.IsKeyPressed(sf::Keyboard::Right))
        {
            c_rot.y+=0.001*frametime;
        }
        if(keyboard.IsKeyPressed(sf::Keyboard::Up))
        {
            c_rot.x+=0.001*frametime;
        }
        if(keyboard.IsKeyPressed(sf::Keyboard::Down))
        {
            c_rot.x-=0.001*frametime;
        }

        if(keyboard.IsKeyPressed(sf::Keyboard::Escape))
        {
            window.Close();
        }

        if(keyboard.IsKeyPressed(sf::Keyboard::B))
        {
            std::cout << "rerr: " << c_pos.x << " " << c_pos.y << " " << c_pos.z << std::endl;
        }

        if(keyboard.IsKeyPressed(sf::Keyboard::V))
        {
            std::cout << frametime << std::endl;
        }



}


void Engine::tick()
{

        sf::Clock t1;
        gpush_lights();
        draw_objects();
        input();
        render_buffers();
        grem_lights();
        render();

}


void Engine::fire_objmanager()
{
    //boost::thread thread(boost::bind(&obj_manager::push_objects, objs));
}

